D:\a\tools.proto\tools.proto\compiler\src\compiler\message.rs
Line | Count | Source |
1 | | // Copyright (c) 2024, BlockProject 3D |
2 | | // |
3 | | // All rights reserved. |
4 | | // |
5 | | // Redistribution and use in source and binary forms, with or without modification, |
6 | | // are permitted provided that the following conditions are met: |
7 | | // |
8 | | // * Redistributions of source code must retain the above copyright notice, |
9 | | // this list of conditions and the following disclaimer. |
10 | | // * Redistributions in binary form must reproduce the above copyright notice, |
11 | | // this list of conditions and the following disclaimer in the documentation |
12 | | // and/or other materials provided with the distribution. |
13 | | // * Neither the name of BlockProject 3D nor the names of its contributors |
14 | | // may be used to endorse or promote products derived from this software |
15 | | // without specific prior written permission. |
16 | | // |
17 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
21 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
23 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
24 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
25 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
26 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | |
29 | | use crate::compiler::builder::FieldBuilder; |
30 | | use crate::compiler::error::Error; |
31 | | use crate::compiler::structure::{FixedFieldType, Structure}; |
32 | | use crate::compiler::union::Union; |
33 | | use crate::compiler::util::objects::name_index; |
34 | | use crate::compiler::util::types::{Name, PtrKey}; |
35 | | use crate::compiler::Protocol; |
36 | | use crate::model::message::MessageFieldValue; |
37 | | use crate::model::protocol::{Description, Endianness}; |
38 | | use crate::model::structure::StructFieldRaw; |
39 | | use bp3d_debug::{error, trace}; |
40 | | use std::cell::Cell; |
41 | | use std::fmt::{Display, Formatter}; |
42 | | use std::rc::Rc; |
43 | | |
44 | | #[derive(Clone, Debug)] |
45 | | pub enum Referenced { |
46 | | Struct(Rc<Structure>), |
47 | | Message(Rc<Message>), |
48 | | } |
49 | | |
50 | | impl Name for Referenced { |
51 | 37 | fn name(&self) -> &str { |
52 | 37 | match self { |
53 | 27 | Referenced::Struct(v) => &v.name, |
54 | 10 | Referenced::Message(v) => &v.name, |
55 | | } |
56 | 37 | } |
57 | | } |
58 | | |
59 | | impl PtrKey for Referenced { |
60 | 63 | fn ptr_key(&self) -> usize { |
61 | 63 | match self { |
62 | 54 | Referenced::Struct(v) => v.ptr_key(), |
63 | 9 | Referenced::Message(v) => v.ptr_key(), |
64 | | } |
65 | 63 | } |
66 | | } |
67 | | |
68 | | impl Referenced { |
69 | 64 | pub fn lookup(proto: &Protocol, reference_name: &str) -> Option<Self> { |
70 | 64 | proto |
71 | 64 | .structs |
72 | 64 | .get(reference_name) |
73 | 64 | .map(|v| Referenced::Struct(v.clone())41 ) |
74 | 64 | .or_else(|| proto.messages.get(reference_name).map(23 |v| Referenced::Message(v.clone())23 )23 ) |
75 | 64 | } |
76 | | } |
77 | | |
78 | | #[derive(Clone, Debug)] |
79 | | pub struct FixedContainerField { |
80 | | pub ty: FixedFieldType, |
81 | | pub item_type: Rc<Structure>, |
82 | | } |
83 | | |
84 | | impl Display for FixedContainerField { |
85 | 10 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
86 | 10 | write!(f, "FixedContainer<{}, Len = {}>", self.item_type.name, self.ty) |
87 | 10 | } |
88 | | } |
89 | | |
90 | | #[derive(Clone, Debug)] |
91 | | pub struct SizedBufferField { |
92 | | pub ty: FixedFieldType, |
93 | | } |
94 | | |
95 | | impl Display for SizedBufferField { |
96 | 14 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
97 | 14 | write!(f, "Varbuf<{}>", self.ty) |
98 | 14 | } |
99 | | } |
100 | | |
101 | | #[derive(Clone, Debug)] |
102 | | pub struct ContainerField { |
103 | | pub ty: FixedFieldType, |
104 | | pub item_type: Rc<Message>, |
105 | | pub nested: bool, |
106 | | } |
107 | | |
108 | | impl Display for ContainerField { |
109 | 25 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
110 | 25 | write!(f, "Container<{}, Len = {}>", self.item_type.name(), self.ty) |
111 | 25 | } |
112 | | } |
113 | | |
114 | | #[derive(Clone, Debug)] |
115 | | pub struct SizedContainerField { |
116 | | pub ty: FixedFieldType, |
117 | | pub item_type: Rc<Message>, |
118 | | pub size_ty: FixedFieldType, |
119 | | } |
120 | | |
121 | | impl Display for SizedContainerField { |
122 | 9 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
123 | 9 | write!( |
124 | 9 | f, |
125 | 9 | "SizedContainer<{}, Len = {}, Size = {}>", |
126 | 9 | self.item_type.name(), |
127 | 9 | self.ty, |
128 | 9 | self.size_ty |
129 | 9 | ) |
130 | 9 | } |
131 | | } |
132 | | |
133 | | #[derive(Clone, Debug)] |
134 | | pub struct FixedField { |
135 | | pub ty: FixedFieldType, |
136 | | } |
137 | | |
138 | | impl Display for FixedField { |
139 | 8 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
140 | 8 | write!(f, "{}", self.ty) |
141 | 8 | } |
142 | | } |
143 | | |
144 | | #[derive(Clone, Debug)] |
145 | | pub struct UnionField { |
146 | | pub r: Rc<Union>, |
147 | | } |
148 | | |
149 | | impl Display for UnionField { |
150 | 8 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
151 | 8 | f.write_str(self.r.name()) |
152 | 8 | } |
153 | | } |
154 | | |
155 | | #[derive(Clone, Debug)] |
156 | | pub enum FieldType { |
157 | | Fixed(FixedField), |
158 | | Ref(Referenced), |
159 | | |
160 | | /// A buffer field is a field which has a known size which can be determined at runtime |
161 | | /// (ex: a null-terminated string). |
162 | | Buffer, |
163 | | |
164 | | /// A sized buffer field is a field which has a known size field based on a configurable type |
165 | | /// (ex: a Varchar). |
166 | | SizedBuffer(SizedBufferField), |
167 | | |
168 | | /// A fixed container is a container which can store only fixed size elements (structures). |
169 | | FixedContainer(FixedContainerField), |
170 | | |
171 | | Union(UnionField), |
172 | | |
173 | | /// A container can store dynamically sized elements. |
174 | | Container(ContainerField), |
175 | | |
176 | | /// A container which has an additional configurable size field to detect the size in bytes of |
177 | | /// the container. |
178 | | SizedContainer(SizedContainerField), |
179 | | |
180 | | Payload, |
181 | | } |
182 | | |
183 | | impl Display for FieldType { |
184 | 125 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
185 | 125 | match self { |
186 | 8 | FieldType::Fixed(v) => v.fmt(f), |
187 | 29 | FieldType::Ref(v) => f.write_str(v.name()), |
188 | 22 | FieldType::Buffer => f.write_str("Buffer"), |
189 | 14 | FieldType::SizedBuffer(v) => v.fmt(f), |
190 | 10 | FieldType::FixedContainer(v) => v.fmt(f), |
191 | 8 | FieldType::Union(v) => v.fmt(f), |
192 | 25 | FieldType::Container(v) => v.fmt(f), |
193 | 9 | FieldType::SizedContainer(v) => v.fmt(f), |
194 | 0 | FieldType::Payload => f.write_str("Bytes"), |
195 | | } |
196 | 125 | } |
197 | | } |
198 | | |
199 | | impl FieldType { |
200 | 55 | pub fn is_message_reference(&self) -> bool { |
201 | 55 | match self { |
202 | 14 | FieldType::Ref(v) => match v { |
203 | 10 | Referenced::Struct(_) => false, |
204 | 4 | Referenced::Message(_) => true, |
205 | | }, |
206 | 41 | _ => false, |
207 | | } |
208 | 55 | } |
209 | | |
210 | 30 | pub fn is_union(&self) -> bool { |
211 | 30 | matches!16 (self, FieldType::Union(_)) |
212 | 30 | } |
213 | | |
214 | 69 | pub fn is_string(&self) -> bool { |
215 | 69 | matches!44 (self, FieldType::SizedBuffer(_) | FieldType::Buffer) |
216 | 69 | } |
217 | | } |
218 | | |
219 | | #[derive(Copy, Clone, Debug)] |
220 | | pub struct SizeInfo { |
221 | | pub is_dyn_sized: bool, |
222 | | pub is_element_dyn_sized: bool, |
223 | | } |
224 | | |
225 | | #[derive(Clone, Debug)] |
226 | | pub struct HeaderField { |
227 | | pub name: String, |
228 | | pub index: usize, |
229 | | } |
230 | | |
231 | | impl HeaderField { |
232 | 76 | fn from_model(header: Option<String>, fields: &[Field]) -> Result<(Option<Self>, Option<&Field>), Error> { |
233 | 76 | match header { |
234 | 12 | Some(header) => { |
235 | 12 | let (index, field11 ) = fields |
236 | 12 | .iter() |
237 | 12 | .enumerate() |
238 | 12 | .find_map(|(k, v)| if v.name == header11 { Some((k, v))11 } else { None0 }11 ) Branch (238:43): [True: 11, False: 0]
Branch (238:43): [Folded - Ignored]
|
239 | 12 | .ok_or(Error::UndefinedReference(header))?1 ; |
240 | 11 | let header = HeaderField { |
241 | 11 | index, |
242 | 11 | name: field.name.clone(), |
243 | 11 | }; |
244 | 11 | Ok((Some(header), Some(field))) |
245 | | } |
246 | 64 | None => Ok((None, None)), |
247 | | } |
248 | 76 | } |
249 | | } |
250 | | |
251 | | #[derive(Clone, Debug)] |
252 | | pub struct Field { |
253 | | pub name: String, |
254 | | pub header: Option<HeaderField>, |
255 | | pub ty: FieldType, |
256 | | pub optional: bool, |
257 | | pub size: SizeInfo, |
258 | | pub endianness: Endianness, |
259 | | pub description: Option<Description>, |
260 | | pub codec: Option<String>, |
261 | | } |
262 | | |
263 | | impl Display for Field { |
264 | 125 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
265 | 125 | match (self.optional, &self.codec) { |
266 | 6 | (true, None) => write!(f, "{}: {}?, {} endian", self.name, self.ty, self.endianness), |
267 | 39 | (false, None) => write!(f, "{}: {}, {} endian", self.name, self.ty, self.endianness), |
268 | 15 | (true, Some(v)) => write!(f, "{}: {} ({})?, {} endian", self.name, v, self.ty, self.endianness), |
269 | 65 | (false, Some(v)) => write!(f, "{}: {} ({}), {} endian", self.name, v, self.ty, self.endianness), |
270 | | } |
271 | 125 | } |
272 | | } |
273 | | |
274 | | impl Field { |
275 | 279 | pub fn codec(&self) -> &str { |
276 | 279 | self.codec.as_deref().unwrap_or("base") |
277 | 279 | } |
278 | | |
279 | 78 | fn from_model( |
280 | 78 | proto: &Protocol, |
281 | 78 | unsorted: &[Field], |
282 | 78 | has_headers: bool, |
283 | 78 | value: crate::model::message::MessageField, |
284 | 78 | ) -> Result<Self, Error> { |
285 | 78 | if (value.value.is_none() && value.item_type.is_none()20 ) || (value.value.is_some()77 && value.item_type.is_some()58 ) Branch (285:13): [True: 20, False: 58]
Branch (285:38): [True: 1, False: 19]
Branch (285:69): [True: 58, False: 19]
Branch (285:94): [True: 1, False: 57]
Branch (285:13): [Folded - Ignored]
Branch (285:38): [Folded - Ignored]
Branch (285:69): [Folded - Ignored]
Branch (285:94): [Folded - Ignored]
|
286 | | { |
287 | 2 | return Err(Error::BadFieldType); |
288 | 76 | } |
289 | 76 | let (header, header_field75 ) = HeaderField::from_model(value.header, unsorted)?1 ; |
290 | 75 | if let Some(field11 ) = header_field { Branch (290:16): [True: 11, False: 64]
Branch (290:16): [Folded - Ignored]
|
291 | 10 | match &field.ty { |
292 | 10 | FieldType::Ref(Referenced::Struct(v)) => v.set_used_in_header(), |
293 | 1 | v => { |
294 | 1 | error!( |
295 | 1 | "Invalid header field type, expected struct reference, got field type {:?}", |
296 | 1 | v |
297 | 1 | ); |
298 | 1 | return Err(Error::InvalidHeaderType); |
299 | | } |
300 | | } |
301 | 64 | } |
302 | 74 | let builder = FieldBuilder::new(value.name, value.optional.unwrap_or_default(), proto.endianness) |
303 | 74 | .description(value.description) |
304 | 74 | .header(header) |
305 | 74 | .codec(value.codec); |
306 | 74 | if let Some(info55 ) = value.value { Branch (306:16): [True: 55, False: 19]
Branch (306:16): [Folded - Ignored]
|
307 | 55 | match info { |
308 | | MessageFieldValue::List { |
309 | 16 | max_len, |
310 | 16 | item_type, |
311 | 16 | max_size, |
312 | 16 | nested, |
313 | 16 | } => { |
314 | 16 | if max_len == 0 { Branch (314:24): [True: 1, False: 15]
Branch (314:24): [Folded - Ignored]
|
315 | 1 | return Err(Error::ZeroArray); |
316 | 15 | } |
317 | 15 | if builder.has_codec() { Branch (317:24): [True: 0, False: 15]
Branch (317:24): [Folded - Ignored]
|
318 | 0 | return Err(Error::ForbiddenCodec(builder.name().into())); |
319 | 15 | } |
320 | 15 | let r = Referenced::lookup(proto, &item_type).ok_or(Error::UndefinedReference(item_type))?0 ; |
321 | 15 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
322 | 15 | match r { |
323 | 4 | Referenced::Struct(item_type) => Ok(builder |
324 | 4 | .codec(Some("list".into())) |
325 | 4 | .build(FieldType::FixedContainer(FixedContainerField { item_type, ty }))), |
326 | 11 | Referenced::Message(item_type) => { |
327 | 11 | if let Some(max_size3 ) = max_size { Branch (327:36): [True: 3, False: 8]
Branch (327:36): [Folded - Ignored]
|
328 | 3 | if max_size == 0 { Branch (328:36): [True: 1, False: 2]
Branch (328:36): [Folded - Ignored]
|
329 | 1 | return Err(Error::ZeroArray); |
330 | 2 | } |
331 | 2 | let size_ty = FixedFieldType::from_max_value(max_size)?0 ; |
332 | 2 | Ok(builder |
333 | 2 | .codec(Some("list".into())) |
334 | 2 | .size_info(SizeInfo { |
335 | 2 | is_element_dyn_sized: false, |
336 | 2 | is_dyn_sized: true, |
337 | 2 | }) |
338 | 2 | .build(FieldType::SizedContainer(SizedContainerField { |
339 | 2 | ty, |
340 | 2 | item_type, |
341 | 2 | size_ty, |
342 | 2 | }))) |
343 | | } else { |
344 | 8 | item_type.embedded.set(true); |
345 | 8 | Ok( |
346 | 8 | builder.codec(Some("list".into())).dynamic_size().build(FieldType::Container( |
347 | 8 | ContainerField { |
348 | 8 | ty, |
349 | 8 | item_type, |
350 | 8 | nested: nested.unwrap_or_default(), |
351 | 8 | }, |
352 | 8 | )), |
353 | 8 | ) |
354 | | } |
355 | | } |
356 | | } |
357 | | } |
358 | | MessageFieldValue::Container { |
359 | 4 | max_len, |
360 | 4 | item_type, |
361 | 4 | max_size, |
362 | 4 | nested, |
363 | 4 | } => { |
364 | 4 | if max_len == 0 { Branch (364:24): [True: 0, False: 4]
Branch (364:24): [Folded - Ignored]
|
365 | 0 | return Err(Error::ZeroArray); |
366 | 4 | } |
367 | 4 | let r = Referenced::lookup(proto, &item_type).ok_or(Error::UndefinedReference(item_type))?0 ; |
368 | 4 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
369 | 4 | match r { |
370 | 0 | Referenced::Struct(item_type) => { |
371 | 0 | Ok(builder.build(FieldType::FixedContainer(FixedContainerField { item_type, ty }))) |
372 | | } |
373 | 4 | Referenced::Message(item_type) => { |
374 | 4 | if let Some(max_size2 ) = max_size { Branch (374:36): [True: 2, False: 2]
Branch (374:36): [Folded - Ignored]
|
375 | 2 | if max_size == 0 { Branch (375:36): [True: 0, False: 2]
Branch (375:36): [Folded - Ignored]
|
376 | 0 | return Err(Error::ZeroArray); |
377 | 2 | } |
378 | 2 | let size_ty = FixedFieldType::from_max_value(max_size)?0 ; |
379 | 2 | Ok(builder |
380 | 2 | .size_info(SizeInfo { |
381 | 2 | is_element_dyn_sized: false, |
382 | 2 | is_dyn_sized: true, |
383 | 2 | }) |
384 | 2 | .build(FieldType::SizedContainer(SizedContainerField { |
385 | 2 | ty, |
386 | 2 | item_type, |
387 | 2 | size_ty, |
388 | 2 | }))) |
389 | | } else { |
390 | 2 | item_type.embedded.set(true); |
391 | 2 | Ok(builder.dynamic_size().build(FieldType::Container(ContainerField { |
392 | 2 | ty, |
393 | 2 | item_type, |
394 | 2 | nested: nested.unwrap_or_default(), |
395 | 2 | }))) |
396 | | } |
397 | | } |
398 | | } |
399 | | } |
400 | 19 | MessageFieldValue::String { max_len } => { |
401 | 19 | if builder.has_codec() { Branch (401:24): [True: 0, False: 19]
Branch (401:24): [Folded - Ignored]
|
402 | 0 | return Err(Error::ForbiddenCodec(builder.name().into())); |
403 | 19 | } |
404 | 19 | match max_len { |
405 | 13 | None => Ok(builder.codec(Some("string".into())).build(FieldType::Buffer)), |
406 | 6 | Some(max_len) => { |
407 | 6 | if max_len == 0 { Branch (407:32): [True: 1, False: 5]
Branch (407:32): [Folded - Ignored]
|
408 | 1 | return Err(Error::ZeroArray); |
409 | 5 | } |
410 | 5 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
411 | 5 | Ok(builder |
412 | 5 | .codec(Some("string".into())) |
413 | 5 | .build(FieldType::SizedBuffer(SizedBufferField { ty }))) |
414 | | } |
415 | | } |
416 | | } |
417 | 4 | MessageFieldValue::Buffer { max_len } => match max_len { |
418 | 0 | None => Ok(builder.build(FieldType::Buffer)), |
419 | 4 | Some(max_len) => { |
420 | 4 | if max_len == 0 { Branch (420:28): [True: 0, False: 4]
Branch (420:28): [Folded - Ignored]
|
421 | 0 | return Err(Error::ZeroArray); |
422 | 4 | } |
423 | 4 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
424 | 4 | Ok(builder.build(FieldType::SizedBuffer(SizedBufferField { ty }))) |
425 | | } |
426 | | }, |
427 | 7 | MessageFieldValue::Union { name } => { |
428 | 7 | let r6 = proto.unions.get(&name).ok_or(Error::UndefinedReference(name))?1 ; |
429 | 6 | let header_field5 = header_field.ok_or(Error::MissingHeaderForUnion)?1 ; |
430 | 5 | match &header_field.ty { |
431 | 5 | FieldType::Ref(Referenced::Struct(v)) => { |
432 | 5 | if !Rc::ptr_eq(&r.discriminant.root, v) { Branch (432:32): [True: 1, False: 4]
Branch (432:32): [Folded - Ignored]
|
433 | 1 | error!( |
434 | 1 | "Union discriminant type mismatch, expected {}, got {}", |
435 | 1 | v.name, r.discriminant.root.name |
436 | 1 | ); |
437 | 1 | return Err(Error::UnionTypeMismatch); |
438 | 4 | } |
439 | | } |
440 | 0 | _ => unreachable!(), |
441 | | } |
442 | 4 | if value.optional.unwrap_or_default() { Branch (442:24): [True: 0, False: 4]
Branch (442:24): [Folded - Ignored]
|
443 | 0 | eprintln!("WARNING: ignoring unsupported optional flag on union message field!"); |
444 | 4 | } |
445 | 4 | Ok(builder.size_info(r.size).build(FieldType::Union(UnionField { r: r.clone() }))) |
446 | | } |
447 | 0 | MessageFieldValue::Payload => Ok(builder.dynamic_size().build(FieldType::Payload)), |
448 | 5 | MessageFieldValue::Unsigned { bits } => { |
449 | 5 | let ty = FixedFieldType::from_model(StructFieldRaw::Unsigned { bits })?0 ; |
450 | 5 | Ok(builder.fixed_size().build(FieldType::Fixed(FixedField { ty }))) |
451 | | } |
452 | | } |
453 | | } else { |
454 | 19 | let item_type = unsafe { value.item_type.unwrap_unchecked() }; |
455 | 19 | let r = Referenced::lookup(proto, &item_type).ok_or(Error::UndefinedReference(item_type))?0 ; |
456 | 19 | match r { |
457 | 15 | Referenced::Struct(r) => { |
458 | 15 | let is_single = r.fields.len() == 1; |
459 | 15 | let is_fixed = r.fields[0].ty.as_fixed().is_some(); |
460 | 15 | let is_none = r.fields[0].ty.as_fixed().map(|v| v.raw.is_none()).unwrap_or_default(); |
461 | 15 | let is_byte_aligned = r.fields[0].loc.bit_size % 8 == 0; |
462 | 15 | trace!({has_headers} {is_single} {is_fixed} {is_none} {is_byte_aligned}, "Found struct reference: {}", r.name); |
463 | 15 | if !has_headers && is_single7 && is_fixed3 && is_none3 && is_byte_aligned3 { Branch (463:24): [True: 7, False: 8]
Branch (463:40): [True: 3, False: 4]
Branch (463:53): [True: 3, False: 0]
Branch (463:65): [True: 3, False: 0]
Branch (463:76): [True: 3, False: 0]
Branch (463:24): [Folded - Ignored]
Branch (463:40): [Folded - Ignored]
Branch (463:53): [Folded - Ignored]
Branch (463:65): [Folded - Ignored]
Branch (463:76): [Folded - Ignored]
|
464 | 3 | let fixed = unsafe { r.fields[0].ty.as_fixed().unwrap_unchecked() }; |
465 | 3 | Ok(builder.fixed_size().build(FieldType::Fixed(FixedField { ty: fixed.bits_type }))) |
466 | | } else { |
467 | 12 | Ok(builder.fixed_size().build(FieldType::Ref(Referenced::Struct(r)))) |
468 | | } |
469 | | } |
470 | 4 | Referenced::Message(r) => Ok(builder.size_info(r.size).build(FieldType::Ref(Referenced::Message(r)))), |
471 | | } |
472 | | } |
473 | 78 | } |
474 | | } |
475 | | |
476 | | #[derive(Clone, Debug)] |
477 | | pub struct Message { |
478 | | pub name: String, |
479 | | pub description: Option<Description>, |
480 | | pub fields: Vec<Field>, |
481 | | pub size: SizeInfo, |
482 | | embedded: Cell<bool>, |
483 | | } |
484 | | |
485 | | impl Message { |
486 | 33 | pub(crate) fn is_embedded(&self) -> bool { |
487 | 33 | self.embedded.get() |
488 | 33 | } |
489 | | |
490 | 44 | pub fn from_model(proto: &Protocol, value: crate::model::message::Message) -> Result<Message, Error> { |
491 | 44 | let mut fields = Vec::with_capacity(value.fields.len()); |
492 | 44 | let mut dyn_sized_elem_count = 0; |
493 | 44 | let mut is_dyn_sized = false; |
494 | 76 | let has_headers = value.fields.iter().any(|v| v.header.is_some())44 ; |
495 | 112 | for v78 in value.fields { |
496 | 78 | let field68 = Field::from_model(proto, &fields, has_headers, v)?10 ; |
497 | 68 | if field.size.is_dyn_sized { Branch (497:16): [True: 48, False: 20]
Branch (497:16): [Folded - Ignored]
|
498 | 48 | is_dyn_sized = true; |
499 | 48 | }20 |
500 | 68 | if dyn_sized_elem_count > 0 && (field.size.is_dyn_sized0 || field.size.is_element_dyn_sized0 ) { Branch (500:16): [True: 0, False: 68]
Branch (500:45): [True: 0, False: 0]
Branch (500:72): [True: 0, False: 0]
Branch (500:16): [Folded - Ignored]
Branch (500:45): [Folded - Ignored]
Branch (500:72): [Folded - Ignored]
|
501 | 0 | return Err(Error::VarsizeAfterPayload); |
502 | 68 | } |
503 | 68 | if field.size.is_element_dyn_sized { Branch (503:16): [True: 10, False: 58]
Branch (503:16): [Folded - Ignored]
|
504 | 10 | dyn_sized_elem_count += 1; |
505 | 58 | } |
506 | 68 | if dyn_sized_elem_count > 1 { Branch (506:16): [True: 0, False: 68]
Branch (506:16): [Folded - Ignored]
|
507 | 0 | return Err(Error::MultiPayload); |
508 | 68 | } |
509 | 68 | fields.push(field); |
510 | | } |
511 | 34 | Ok(Message { |
512 | 34 | name: value.name, |
513 | 34 | description: value.description, |
514 | 34 | fields, |
515 | 34 | size: SizeInfo { |
516 | 34 | is_dyn_sized, |
517 | 34 | is_element_dyn_sized: dyn_sized_elem_count > 0, |
518 | 34 | }, |
519 | 34 | embedded: Cell::new(false), |
520 | 34 | }) |
521 | 44 | } |
522 | | } |
523 | | |
524 | | name_index!(Message => name); |